package com.ouc.openplatform.pampas;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Status;
import net.sf.ehcache.cluster.CacheCluster;
import net.sf.ehcache.cluster.ClusterNode;
import net.sf.ehcache.cluster.ClusterScheme;
import net.sf.ehcache.cluster.ClusterTopologyListener;
import org.springframework.cache.Cache;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.cache.ehcache.EhCacheCache;
import org.springframework.cache.support.AbstractCacheManager;
import org.springframework.cache.support.SimpleCacheManager;
import org.springframework.util.Assert;

public class EhcacheCacheManagerProxy extends AbstractCacheManager
{
  private CacheManager cacheManager;
  private SimpleCacheManager simpleCacheManager = new SimpleCacheManager();
  private List<ClusterTopologyListener> listenerList;
  private boolean globalFlag = true;
  private boolean innerFlag = true;
  
  public CacheManager getCacheManager()
  {
    return this.cacheManager;
  }
  
  public void setCacheManager(CacheManager cacheManager)
  {
    this.cacheManager = cacheManager;
  }
  
  public void setGlobalFlag(boolean globalFlag)
  {
    this.globalFlag = globalFlag;
  }
  
  public void setListenerList(List<ClusterTopologyListener> listenerList)
  {
    this.listenerList = listenerList;
  }
  
  public void afterPropertiesSet()
  {
    initSimpleCacheManager();
    if (!this.globalFlag) {
      return;
    }
    super.afterPropertiesSet();
  }
  
  protected Collection<Cache> loadCaches()
  {
    Assert.notNull(this.cacheManager, "A backing EhCache CacheManager is required");
    Status status = this.cacheManager.getStatus();
    Assert.isTrue(Status.STATUS_ALIVE.equals(status), "An 'alive' EhCache CacheManager is required - current cache is " + status.toString());
    

    String[] names = this.cacheManager.getCacheNames();
    Collection<Cache> caches = new LinkedHashSet<Cache>(names.length);
    for (String name : names) {
      caches.add(new EhCacheCache(this.cacheManager.getEhcache(name)));
    }
    addClusterListener();
    
    return caches;
  }
  
  public Cache getCache(String name)
  {
    if ((!this.globalFlag) || (!this.innerFlag)) {
      return this.simpleCacheManager.getCache("simpleCache");
    }
    Cache cache = super.getCache(name);
    if (cache == null)
    {
      Ehcache ehcache = this.cacheManager.getEhcache(name);
      if (ehcache != null)
      {
        cache = new EhCacheCache(ehcache);
        addCache(cache);
      }
    }
    return cache;
  }
  
  private void addClusterListener()
  {
    if (this.listenerList == null) {
      this.listenerList = new ArrayList<ClusterTopologyListener>();
    }
    this.listenerList.add(new ClusterTopologyListener()
    {
      public void nodeLeft(ClusterNode node)
      {
        EhcacheCacheManagerProxy.this.innerFlag = true;
      }
      
      public void nodeJoined(ClusterNode node)
      {
        EhcacheCacheManagerProxy.this.innerFlag = true;
      }
      
      public void clusterRejoined(ClusterNode oldNode, ClusterNode newNode)
      {
        EhcacheCacheManagerProxy.this.innerFlag = true;
      }
      
      public void clusterOnline(ClusterNode node)
      {
        EhcacheCacheManagerProxy.this.innerFlag = true;
      }
      
      public void clusterOffline(ClusterNode node)
      {
        EhcacheCacheManagerProxy.this.innerFlag = false;
      }
    });
    CacheCluster cluster = this.cacheManager.getCluster(ClusterScheme.TERRACOTTA);
    for (ClusterTopologyListener listener : this.listenerList) {
      if (listener != null) {
        cluster.addTopologyListener(listener);
      }
    }
  }
  
  private void initSimpleCacheManager()
  {
    List<Cache> caches = new ArrayList<Cache>();
    ConcurrentMapCache memoryOnlyCache = new ConcurrentMapCache("simpleCache");
    caches.add(memoryOnlyCache);
    this.simpleCacheManager.setCaches(caches);
    this.simpleCacheManager.afterPropertiesSet();
  }
}
